package org.deyang.tools.amazon.pricetracker;

import java.io.IOException;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;

import org.apache.commons.io.IOUtils;
import org.deyang.tools.amazon.exceptions.KeepaContentException;
import org.deyang.tools.amazon.pojos.DealDetail;
import org.deyang.tools.amazon.pojos.KeepaPriceChangeEntity;
import org.deyang.tools.amazon.pojos.PriceInformation;

public class KeepaPriceTracker {
	
	public static final int AMAZON_SELF = 0;
	public static final int MARKET_NEW = 1;
	
	private String originalKeepaResponseBody;
	private String asinValue;
	private PriceInformation pi;
	private DealDetail dd = new DealDetail();
	//private Calendar keepaStartDate = new GregorianCalendar(2011, Calendar.JANUARY, 1, 7, 40, 0);
	private List<KeepaPriceChangeEntity> amazonPriceHistory;
	private List<KeepaPriceChangeEntity> marketPriceHistory;
	
	public KeepaPriceTracker(String asin, PriceInformation pi){
		this.asinValue = asin;
		if (pi == null){
			this.pi = new PriceInformation();
		}else{
			this.pi = pi;	
		}
		String keepaUri = "http://dyn.keepa.com/21" + asin + "?callback=jQuery17105295578057125696_" + Math.random() * 10000;
		StringWriter writer = new StringWriter();
		try {
			URL keepaurl = new URL(keepaUri);
			URLConnection conn = keepaurl.openConnection();
			conn.setRequestProperty("Accept-Encoding", "gzip");
			
			GZIPInputStream gzipInputStream = new GZIPInputStream(conn.getInputStream());
			IOUtils.copy(gzipInputStream, writer);
		} catch (IOException e) {
			System.err.println(e.getMessage());
			return;
		}
		this.originalKeepaResponseBody = writer.toString();
		String regex = "(?<=\\bAMAZON\\b.{4})[\\d,-]*";  
		Pattern p = Pattern.compile(regex);  
		Matcher m = p.matcher(this.originalKeepaResponseBody);  
		String matchResult = null;
		if (m.find()){
			matchResult = m.group();
			this.amazonPriceHistory = this.generateProcessList(matchResult);
		}
		
		regex = "(?<=\\bMARKET_NEW\\b.{4})[\\d,-]*";
		p = Pattern.compile(regex);  
		m = p.matcher(this.originalKeepaResponseBody);  
		matchResult = null;
		if (m.find()){
			matchResult = m.group();
			this.marketPriceHistory = this.generateProcessList(matchResult);
		}
	}
	


	
	private List<KeepaPriceChangeEntity> generateProcessList(String priceString){
		List<KeepaPriceChangeEntity> result = null;
		if (priceString != null){
			result =  new ArrayList<KeepaPriceChangeEntity>();
			String[] prices = priceString.split(",");
			
			for (int i = 0; i < prices.length; i = i+2){
				//if (!prices[i + 1].equals("-1")){
					KeepaPriceChangeEntity changeEntity = new KeepaPriceChangeEntity();
					changeEntity.setHours(Integer.parseInt(prices[i]));
					changeEntity.setPriceValue(Integer.parseInt(prices[i + 1]));
					result.add(changeEntity);
				//}
			}

		}
		return result;				
		
	}
	
	
	public DealDetail getDealDetail(){
		return this.dd;
		
	}
	
	private boolean isAmazonDeal(int daysPeriod){
		Calendar calendar = new GregorianCalendar(Locale.UK);
		calendar.add(Calendar.HOUR, -(daysPeriod * 24));
		List<KeepaPriceChangeEntity> priceHistory = this.amazonPriceHistory;
		if (!this.pi.isAmazonPriceAvailable()){
			return false;
		}
		
		if (priceHistory != null && priceHistory.size() > 0){
			boolean isPriceInPeriod = false;
			boolean isPriceTrendDown = false;
			int highPrice = 0;
			int lowPrice = 0;
			int edgePrice = 0;
			
			//initial keepa latest price and current amazon price
			int size = priceHistory.size();
			int currentKeepaPrice = priceHistory.get(size - 1).getPriceValue();
			int currentPrice = -1;
			currentPrice = Integer.parseInt(this.pi.getAmazonPriceAmount());
			if (currentPrice > 0 && currentPrice < lowPrice){
				this.dd.setAmazonDeal(true);
				this.dd.setAmazonDealInHot(true);
				return true;
			}
			
			for (int i = 0; i < priceHistory.size(); i++){
				if (!isPriceInPeriod && priceHistory.get(i).getCalendarValue().compareTo(calendar) > 0){
					//find a price change after edge date
					isPriceInPeriod = true;
					if (i > 0){
						// try to get the price at edge date
						highPrice = priceHistory.get(i - 1).getPriceValue();
						lowPrice = highPrice;
						edgePrice = lowPrice;
					}else{
						//if this is first price record, then take it as edge date price
						highPrice = priceHistory.get(i).getPriceValue();
						lowPrice = highPrice;
						edgePrice = lowPrice;
					}
				}
				if (isPriceInPeriod){
					if (priceHistory.get(i).getPriceValue() > highPrice){
						highPrice = priceHistory.get(i).getPriceValue();
					}else if (priceHistory.get(i).getPriceValue() < lowPrice){
						lowPrice = priceHistory.get(i).getPriceValue();
					}
				}
			}
			
			//Check trend
			if (size > 1 && (priceHistory.get(size - 1).getPriceValue() < priceHistory.get(size - 2).getPriceValue())){
				isPriceTrendDown = true;
			}			
			
			if (isPriceTrendDown 
					&& currentKeepaPrice <= lowPrice 
					&& (lowPrice < highPrice * 0.97 || (highPrice - lowPrice) > 500)
					&& currentKeepaPrice < edgePrice
					&& currentPrice <= lowPrice){
				//check whether it is a hotdeal
				if (this.isHotDeal(priceHistory.get(size - 1).getHours()) > 0){
					this.dd.setAmazonDealInHot(true);
				}
				this.dd.setAmazonDeal(true);
				return true;
			}
			
			
		}else{
			System.out.println("We have no amazon price history information:" + this.asinValue);
			return false;
		}
		return false;
		
	}
	
	private boolean isMarketDeal(int daysPeriod){
		Calendar calendar = new GregorianCalendar(Locale.UK);
		calendar.add(Calendar.HOUR, -(daysPeriod * 24));
		List<KeepaPriceChangeEntity> priceHistory = this.marketPriceHistory;
		if (!this.pi.isThirdPartyPriceAvailable()){
			return false;
		}
		
		if (priceHistory != null && priceHistory.size() > 0){
			boolean isPriceInPeriod = false;
			boolean isPriceTrendDown = false;
			int highPrice = 0;
			int lowPrice = 0;
			int edgePrice = 0;
			
			//initial keepa latest price and current price
			int size = priceHistory.size();
			int currentKeepaPrice = priceHistory.get(size - 1).getPriceValue();
			int currentPrice = -1;
			currentPrice = Integer.parseInt(this.pi.getThirdPartyPriceAmount());
			if (currentPrice > 0 && currentPrice < currentKeepaPrice){
				this.dd.setMarketDeal(true);
				this.dd.setMarketDealInHot(true);
				return true;
			}
			
			for (int i = 0; i < priceHistory.size(); i++){
				if (!isPriceInPeriod && priceHistory.get(i).getCalendarValue().compareTo(calendar) > 0){
					//find a price change after edge date
					isPriceInPeriod = true;
					if (i > 0){
						// try to get the price at edge date
						highPrice = priceHistory.get(i - 1).getPriceValue();
						lowPrice = highPrice;
						edgePrice = lowPrice;
					}else{
						//if this is first price record, then take it as edge date price
						highPrice = priceHistory.get(i).getPriceValue();
						lowPrice = highPrice;
						edgePrice = lowPrice;
					}
				}
				if (isPriceInPeriod){
					if (priceHistory.get(i).getPriceValue() > highPrice){
						highPrice = priceHistory.get(i).getPriceValue();
					}else if (priceHistory.get(i).getPriceValue() < lowPrice){
						lowPrice = priceHistory.get(i).getPriceValue();
					}
				}
			}
			
			//Check trend
			if (size > 1 && (priceHistory.get(size - 1).getPriceValue() < priceHistory.get(size - 2).getPriceValue())){
				isPriceTrendDown = true;
			}		
			
			if (!isPriceInPeriod){
				//有些时候曲线最后一段长时间不变，这样的将最后一个keepa price作为在低价
				lowPrice = currentKeepaPrice;
			}
			
			if (isPriceTrendDown && currentKeepaPrice <= lowPrice && lowPrice < highPrice && currentKeepaPrice < edgePrice){
				//check whether it is a hotdeal
				if (this.isHotDeal(priceHistory.get(size - 1).getHours()) > 0){
					this.dd.setMarketDealInHot(true);
				}
				this.dd.setMarketDeal(true);
				return true;
			}
			
			
		}else{
			System.out.println("We have no third party price history information:" + this.asinValue);
			return false;
		}
		return false;
		
	}
	
	private int isHotDeal(int lastChangeHours){
		Calendar currentCal = new GregorianCalendar(Locale.UK);
		Calendar keepaStartDate = new GregorianCalendar(2011, Calendar.JANUARY, 1, 7, 40, 0);
		keepaStartDate.add(Calendar.HOUR, lastChangeHours + 6 * 24);
		return keepaStartDate.compareTo(currentCal);
		
	}
	

	public boolean isDeal(int daysPeriod){
		if (!this.pi.isAmazonPriceAvailable() && !this.pi.isThirdPartyPriceAvailable()){
			//No price information, so return false
			return false;
		}
		if (this.isAmazonDeal(daysPeriod) || this.isMarketDeal(daysPeriod)){
			return true;
		}
		return false;
		
	}

}
